#!/usr/bin/env python
# encoding: utf-8

"""
@version: python3.7
@Author  : huangyi
@contact:  hy.dazui@qq.com
@Time    : 2020/1/12 23:11
@Describe: add Describe here
"""
from enum import Enum

from app.scanner.token.token import TokenType, Token, EndToken
from app.scanner.token.token_factory import TokenFactory


class State(Enum):
    NUMBER = 1  # 数字
    OP = 2  # 运算符
    LEFT_BRACKET = 3  # 左括号
    RIGHT_BRACKET = 4  # 右括号
    ZERO = 5
    POINT = 6  # 小数
    POINT_OP = 7  # 小数点
    FUNC = 8  # 方法


class Scanner:

    def __init__(self):
        self.data = []

    def scan(self, expression: str) -> list:
        if not expression:
            return []
        last_state = None
        token_list = []
        for index, char in enumerate(expression):
            if char == ' ':
                # 跳过空格
                continue
            if char == '-':
                if not last_state:
                    last_state = State.NUMBER
                elif last_state == State.OP:
                    # 在操作符后面的一定负号或第一个符号
                    token_list.append(self.build_token(last_state))
                    last_state = State.NUMBER
                elif last_state in [State.NUMBER, State.ZERO, State.POINT]:
                    # 当前状态是数值，则这时候负号是减号
                    token_list.append(self.build_token(last_state))
                    last_state = State.OP
                else:
                    self.throw_error(char, index)
            elif char == '(':
                if not last_state:
                    last_state = State.LEFT_BRACKET
                elif last_state == State.OP:
                    token_list.append(self.build_token(last_state))
                    last_state = State.LEFT_BRACKET
                elif last_state == State.LEFT_BRACKET:
                    token_list.append(self.build_token(last_state))
                    last_state = State.LEFT_BRACKET
                else:
                    self.throw_error(char, index)
            elif char == ')':
                if last_state in [State.NUMBER, State.ZERO, State.POINT]:
                    token_list.append(self.build_token(last_state))
                    last_state = State.RIGHT_BRACKET
                elif last_state == State.RIGHT_BRACKET:
                    token_list.append(self.build_token(last_state))
                    last_state = State.RIGHT_BRACKET
                else:
                    self.throw_error(char, index)
            elif char == '+':
                if last_state == State.RIGHT_BRACKET:
                    token_list.append(self.build_token(last_state))
                    last_state = State.OP
                elif last_state in [State.NUMBER, State.ZERO, State.POINT]:
                    token_list.append(self.build_token(last_state))
                    last_state = State.OP
                else:
                    self.throw_error(char, index)
            elif char == '*':
                if last_state == State.RIGHT_BRACKET:
                    token_list.append(self.build_token(last_state))
                    last_state = State.OP
                elif last_state in [State.NUMBER, State.ZERO, State.POINT]:
                    token_list.append(self.build_token(last_state))
                    last_state = State.OP
                else:
                    self.throw_error(char, index)
            elif char == '&':
                if last_state == State.RIGHT_BRACKET:
                    token_list.append(self.build_token(last_state))
                    last_state = State.OP
                elif last_state in [State.NUMBER, State.ZERO, State.POINT]:
                    token_list.append(self.build_token(last_state))
                    last_state = State.OP
                elif last_state == State.OP:
                    pass
            elif char == '|':
                if last_state == State.RIGHT_BRACKET:
                    token_list.append(self.build_token(last_state))
                    last_state = State.OP
                elif last_state in [State.NUMBER, State.ZERO, State.POINT]:
                    token_list.append(self.build_token(last_state))
                    last_state = State.OP
                elif last_state == State.OP:
                    pass
            elif char == '/':
                if last_state == State.RIGHT_BRACKET:
                    token_list.append(self.build_token(last_state))
                    last_state = State.OP
                elif last_state in [State.NUMBER, State.ZERO, State.POINT]:
                    token_list.append(self.build_token(last_state))
                    last_state = State.OP
                else:
                    self.throw_error(char, index)
            elif char == '0':
                if not last_state:
                    last_state = State.NUMBER
                elif last_state == State.OP:
                    # 最后一个状态是操作符，这里0只能是单个0
                    token_list.append(self.build_token(last_state))
                    last_state = State.ZERO
                elif last_state in [State.NUMBER, State.POINT]:
                    pass
                elif last_state == State.POINT_OP:
                    last_state = State.POINT
                else:
                    self.throw_error(char, index)
            elif '1' <= char <= '9':
                if not last_state:
                    last_state = State.NUMBER
                elif last_state in [State.NUMBER, State.POINT]:
                    pass
                elif last_state == State.POINT_OP:
                    last_state = State.POINT
                elif last_state in [State.OP, State.LEFT_BRACKET]:
                    token_list.append(self.build_token(last_state))
                    last_state = State.NUMBER
                else:
                    self.throw_error(char, index)
            elif char == '.':
                if last_state in [State.NUMBER, State.ZERO]:
                    last_state = State.POINT_OP
                else:
                    self.throw_error(char, index)

            else:
                self.throw_error(char, index)
            self.data.append(char)
        # 处理最后的数据
        if self.data:
            token_list.append(self.build_token(last_state))
        # 放入结束标志
        token_list.append(EndToken())
        return token_list

    def throw_error(self, char, index):
        raise Exception("char '{}' at pos:{} is invalid!".format(char, index))

    def build_token(self, state: State):
        token = None
        if state == State.RIGHT_BRACKET:
            token = TokenFactory.build_token(TokenType.RIGHT_BRACKET, "".join(self.data))
        elif state == State.LEFT_BRACKET:
            token = TokenFactory.build_token(TokenType.LEFT_BRACKET, "".join(self.data))
        elif state in [State.NUMBER, State.ZERO]:
            token = TokenFactory.build_token(TokenType.NUMBER, "".join(self.data))
        elif state == State.POINT:
            token = TokenFactory.build_token(TokenType.POINT, "".join(self.data))
        elif state == State.OP:
            token = TokenFactory.build_token(TokenType.OP, "".join(self.data))
        else:
            raise Exception("unsupported state {}".format(state))
        self.data = []
        return token

